浏览器客户端
Ellen Spertus(spertus@google.com)
本文档描述了在用户的Web浏览器中运行的App Inventor部分,即appengine子项目的客户端和共享部分。这不包括Blocks Editor代码,它当前在单独的JNLP任务中运行,但正在移动到浏览器中。
Google Web Toolkit(GWT) 允许程序员用Java编写客户端 - 服务器应用程序,而不必担心远程过程调用(RPC)的细节,除了为RPC提供显式回调(一个用于成功调用,一个用于失败)。GWT将客户端代码编译为JavaScript,在JavaScript中运行,并且RPC在服务器上作为Java代码运行,通过HTTP完成通信。建议读者 在深入研究运行的App Inventor部分或响应用户浏览器的请求之前了解GWT。
Google App Engine (GAE)是一个云计算平台,支持用Java(或Python)编写的程序在Google服务器上运行和维护数据。App Inventor的原始内部版本直接构建在专有的Google基础架构上,但对于开源版本,使用第三方Objectify 数据存储API 重写为使用GAE 。
GWT和GAE可以很好地协同工作,GWT服务器可以在GAE服务器上运行。这就是App Inventor的工作方式,如图1所示。
图1:App Inventor客户端和服务器的框图。
两者都是使用GWT创建的,GWT将前端代码转换为JavaScript,后者在用户的浏览器中使用GWT客户端库运行。后端作为Google App Engine服务在GWT服务器库上运行,使用第三方Objectify API进行数据存储。
App Engine客户端YaClientApp依赖于:
图2显示了App Inventor子项目之间的动态交互。当浏览器客户端启动时,各种信息(在 下面的启动行为部分中详细讨论)将从App Engine服务器传递到客户端。这包括服务器构建ID和当天的消息(motd)。有关项目组件,资产和用户设置的信息 在两个方向上流动。
当块编辑器通过Java WebStart文件(未显示)从客户端启动时,块编辑器请求并接收当前项目的组件信息 并 阻止代码。在任何更改之后,Blocks Editor会将块代码发送 回服务器。在客户端中添加,删除或重命名 组件时,客户端会将更新的组件信息发送 到服务器,并通知Blocks Editor已对组件进行了更改。然后,块编辑器请求并接收更新的组件信息 从服务器。执行此多步更新是因为只能通过客户端和块编辑器之间的HTTP连接传递少量信息。可以在CodeblocksConnection中找到客户端发送到Blocks Editor的完整消息列表。
如果用户建立了从块编辑器到设备(或仿真器)的连接,则块编辑器请求并接收AiPhoneApp(提供“repl”解释器的APK)和当前项目的资产,并将其传递给设备。
如果用户在客户端请求构建应用程序,则客户端向服务器发送请求,该请求将组件信息,块代码和密钥库打包 成zip文件,然后将其发送到构建服务器。在尝试构建应用程序之后,构建服务器将状态/错误消息和APK 发送到App Engine服务器,后者保存APK的副本并将其发送给客户端。如果初始请求是构建应用程序并将其下载到手机(而不是生成条形码或将其下载到计算机),则客户端会向Blocks Editor发送消息,然后Blocks Editor会从中请求并接收APK 。 App Engine服务器。
图2:App Inventor子项目之间的运行时交互。不显示数据请求。
客户端发送到Blocks Editor的通知会提醒服务器更新属性,组件,项目或APK。
有关App Engine(浏览器)客户端和服务器的更多详细信息,请参见图1。
主要面板如图3所示。未显示的是RootPanel,它由GWT提供并填充应用程序窗口。其余面板在Ode.initializeUi()中创建。第一个要创建的是mainPanel,它填充了RootPanel。它的孩子类型:
图3:在Ode.initializeUi()中创建的面板,以洋红色标出并标记。
最外面的面板mainPanel是DockPanel类型 ,包含三个面板:TopPanel,DeckPanel和StatusPanel。无论视图是“项目”选项卡,“设计”选项卡还是“调试”选项卡,都会显示TopPanel和StatusPanel。图片的其余部分是DeckPanel(人为地减小了尺寸)。它有三个孩子; 显示的那个取决于所选的选项卡。
Design选项卡(如图4所示)是用户向项目添加组件,指定其属性和位置以及管理媒体文件(资产)的位置。
图4:“设计”选项卡视图。主VerticalPanel以洋红色显示,方框以蓝色显示,DesignToolbar以深紫色显示。
此屏幕上有5个框可见:MotdBox,PaletteBox,ViewerBox,SourceStructureBox,PropertiesBox和AssetListBox。底部的StatusPanel已被省略。
图4中突出显示的所有框类都实现了单例模式, 并直接扩展了抽象类com.google.appinventor.client.widgets.boxes.Box,这是一个构建在com.google.gwt.user.client之上的容器小部件。 ui.FlowPanel ,可自动处理嵌入式窗口小部件的滚动,并可进行调整大小,最小化和恢复。每个盒子都有一个标题,可以在OdeMessages中找到。这些框以私有方法Ode.initializeUi()添加到GUI中。
所述MotdBox 显示当天(MOTD)的消息。在启动期间,方法Ode.setupMotd()调用GetMotdService.getCheckInterval() RPC以查找检查MOTD更新的频率。一旦检索到响应,客户端就会构造一个MotdFetcher,这会调用它的fetchMotd()方法,从而调用MotdService.getMotd() 请求。检索到MOTD时,调用MotdUi.setMotd() 来显示MOTD。
所述PaletteBox 包含单个元件,YoungAndroidPalettePanel,一个复合 含有的StackPanel,其中“垂直堆叠其孩子,只有一次一个显示,对于每个子,用户可以点击以显示标题”。在图2中,正在显示“基本”类别。
所述ViewerBox 包含ProjectEditor (具体地,YaProjectEditor)。EditorManager维护多个ProjectEditor实例,每个实例对应于当前浏览器会话中打开的每个项目。反过来,每个ProjectEditor都为项目中的每个屏幕都有一个FileEditor。这在图5中说明。
图5还显示了每个YaProjectEditor都引用了YaFormEditor,它管理GWT(拖放和删除)与框和面板之间的大部分通信。例如,如果将新组件拖到YaFormEditor(YaFormEditor.onComponentAdded())上,则SourceStructureBox和PropertiesBox都会收到通知。
为了提供真实组件的“模拟”表示,例如图4中的屏幕,按钮和声音,有一个以MockComponent类为根的模拟组件的层次结构。不可见的组件(如Sound)由MockNonVisibleComponent表示,它具有最小的功能。 MockButton 是更复杂的模拟组件之一,因为它的外观取决于其属性的值,例如Text,Image和BackgroundColor。
图5:ProjectEditor的静态UML类图。有一个EditorManager,它维护一个ProjectEditor实例的集合,每个项目对应于此浏览器会话中打开的项目。每个ProjectEditor都维护一个FileEditor实例集合,一个用于项目中的每个屏幕。ProjectEditor还维护图6中所示的GUI元素。
图6:ViewerBox,GUI组件标记为(左),未标记(右)。
ViewerBox包含ScrollBar 中的TabBar 和DeckPanel ,其中包含SimpleNonVisibleComponentsPanel 和SimpleVisibleComponentsPanel ,其中包含单个VerticalPanel 。VerticalPanel包含一个CheckBox和一个MockForm ,它包含一个MockForm.PhoneBar,一个MockForm.TitleBar和 一个ScrollPanel中的AbsolutePanel 。左侧未显示(因为它们不存在于所有项目中)是MockForm中的MockButton 和MockNonVisibleComponent中的MockButton SimpleNonVisibleComponentsPanel 。
SourceStructureBox中的唯一项 是SourceStructureExplorer,如图6所示,它显示项目组件的分层视图。它被实现为一个树 ,其中每个TreeItem 包含一个SourceStructureExplorerItem。
该AssetListBox 列出了程序员上传的资产(媒体文件)。它包含一个AssetList,它扩展了Composite 并有一个私有成员assetList,它是一个Tree ,其中每个TreeItem 包含一个ProjectNode - 特别是YoungAndroidAssetNode (图7)。在CommandRegistry中指定的节点被选中时可用的上下文菜单命令是DeleteFileCommand 和DownloadFileCommand。
图7:ProjectNode的直接和间接子类。
未显示的是由YoungAndroidProjectNode实现的接口HasAssetFolder <YoungAndroidAssetsFolder>。
与其他框一样,PropertiesBox是在Ode.initializeUi()中创建的,尽管在选择组件之前不会填充它。具体来说,构造YaFormEditor时,它会创建一个新的PropertiesPanel并保存名为designProperties的私有引用。发生以下任何情况时,将更新此面板的内容:
Projects选项卡(如图8所示)是用户创建,删除,打开,上载或下载项目和密钥库的位置。ProjectToolbar位于顶部,包含四个ToolbarItem实例,每个实例都有一个名称(例如,“DownloadAll”),一个标题(例如,“Download All Projects”和一个Command(例如,ProjectToolbar.NewAction)。)ProjectListBox由ProjectList填充,其中包含一个表,其中包含可用于排序的“Name”和“Date Created”编辑器,以及包含CheckBox,项目名称和创建日期的行。
图8:项目选项卡视图。它由一个包含ProjectToolbar(洋红色)的VerticalPanel和一个包含ProjectListBox的HorizontalPanel组成。ProjectToolbar扩展了工具栏,它扩展了Composite,由一个包含两个HorizontalPanels的HorizontalPanel组成,名为leftButtons和RightButtons(当前未使用)。ProjectListBox填充了ProjectList,它扩展了Composite,包含一个包含Grid的VerticalPanel ,其“Name”和“Date Created”标题是HorizontalPanels,每个都支持在任一方向排序。每行包括一个的复选框,一个标签 包含项目名称和标签 包含该项目的创建日期。
仅当AppInventorFeatures.hasDebuggingView()返回true时,才会提供“调试”选项卡(图8)。它由一个包含两个框的WorkAreaPanel组成。第一个是MessagesOutputBox,它显示传递给MessagesOutput.addMessages()的消息,这些消息是关于构建应用程序并将其下载到手机的请求的状态。OdeLogBox显示通过以下静态方法之一记录的消息的输出:
图9:调试选项卡视图。在上面的框中,MessagesOutputBox显示来自BuildServer的消息。
下方框OdeLogBox显示客户端代码中生成的日志消息。
图10:Debugging选项卡组件的静态UML类图。
UI元素以蓝色显示。未显示(因为它们不是App Inventor代码)是ColumnLayout.Column的超类,
它是AbstractIndexedDropController,以及Widget的超类,它是UIObject。
MessagesOutputBox和OdeLogBox都是Box的子类。
图11:启动信息流和控制流程。
RPC在客户端(左)和服务器(右)之间用水平箭头表示,其中查询和响应具有相同的颜色和初始编号。彩色虚线箭头表示在收到相关响应后发生的本地方法和构造函数调用。黑色虚线表示由于听众而导致的本地方法调用。例如,Ode.onModuleLoad()生成一个RPC(2Q)。收到响应后,它会发出三个本地调用:UserSettings.loadSettings(),ProjectManager()和Ode.initializeUi(),它们调用Ode.setupMotd()。
图11显示了用户开始App Inventor会话时的信息和控制流程(假设没有RPC故障或其他异常事件)。请注意,文档的这一部分比其他部分更容易改变。
为了便于讨论,我们假设查询3Q,4Q和5Q按照它们的发布顺序进行回答,但响应可以按任何顺序进行。
后续控制流和RPC取决于用户的行为。